<!DOCTYPE html><html lang="zh-Hans"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"><meta name="description" content="Nodejs express koa2初学习"><meta name="keywords" content="nodejs express koa2"><meta name="author" content="晟松"><meta name="copyright" content="晟松"><title>Nodejs express koa2初学习 | 晟松</title><link rel="shortcut icon" href="/logo.png"><link rel="stylesheet" href="/css/index.css?version=1.9.0"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@latest/css/font-awesome.min.css?version=1.9.0"><meta name="format-detection" content="telephone=no"><meta http-equiv="x-dns-prefetch-control" content="on"><link rel="dns-prefetch" href="https://cdn.jsdelivr.net"><link rel="dns-prefetch" href="https://hm.baidu.com"><script>var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?c34d81d53341febe06a155941e2df16d";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();</script><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><script>var GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"search.xml","languages":{"hits_empty":"找不到您查询的内容:${query}"}},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  hexoVersion: '5.4.0'
} </script><meta name="generator" content="Hexo 5.4.0"></head><body><i class="fa fa-arrow-right" id="toggle-sidebar" aria-hidden="true"></i><div id="sidebar" data-display="true"><div class="toggle-sidebar-info text-center"><span data-toggle="切换文章详情">切换站点概览</span><hr></div><div class="sidebar-toc"><div class="sidebar-toc__title">目录</div><div class="sidebar-toc__progress"><span class="progress-notice">你已经读了</span><span class="progress-num">0</span><span class="progress-percentage">%</span><div class="sidebar-toc__progress-bar"></div></div><div class="sidebar-toc__content"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#node"><span class="toc-number">1.</span> <span class="toc-text">node</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%AE%80%E5%8D%95%E4%BB%8B%E7%BB%8D"><span class="toc-number">1.1.</span> <span class="toc-text">简单介绍</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B8%B8%E7%94%A8%E5%86%85%E7%BD%AE%E6%A8%A1%E5%9D%97"><span class="toc-number">1.2.</span> <span class="toc-text">常用内置模块</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#os%E6%A8%A1%E5%9D%97"><span class="toc-number">1.2.1.</span> <span class="toc-text">os模块</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#path%E6%A8%A1%E5%9D%97"><span class="toc-number">1.2.2.</span> <span class="toc-text">path模块</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#url%E6%A8%A1%E5%9D%97"><span class="toc-number">1.2.3.</span> <span class="toc-text">url模块</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#querystring%E6%A8%A1%E5%9D%97"><span class="toc-number">1.2.4.</span> <span class="toc-text">querystring模块</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#fs%E6%A8%A1%E5%9D%97"><span class="toc-number">1.2.5.</span> <span class="toc-text">fs模块</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#http%E6%A8%A1%E5%9D%97"><span class="toc-number">1.2.6.</span> <span class="toc-text">http模块</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E6%9C%8D%E5%8A%A1%E5%99%A8"><span class="toc-number">1.2.6.1.</span> <span class="toc-text">静态资源服务器</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#get"><span class="toc-number">1.2.6.2.</span> <span class="toc-text">get</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#post"><span class="toc-number">1.2.6.3.</span> <span class="toc-text">post</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%B3%A8%E6%84%8F%E7%82%B9"><span class="toc-number">1.3.</span> <span class="toc-text">注意点</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#express"><span class="toc-number">2.</span> <span class="toc-text">express</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%AE%80%E5%8D%95%E4%BB%8B%E7%BB%8D-1"><span class="toc-number">2.1.</span> <span class="toc-text">简单介绍</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9D%E5%A7%8B%E5%8C%96%E9%A1%B9%E7%9B%AE"><span class="toc-number">2.1.1.</span> <span class="toc-text">初始化项目</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%9B%E5%BB%BAweb%E6%9C%8D%E5%8A%A1"><span class="toc-number">2.2.</span> <span class="toc-text">创建web服务</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%8E%B7%E5%8F%96query"><span class="toc-number">2.2.1.</span> <span class="toc-text">获取query</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8A%A8%E6%80%81%E5%8F%82%E6%95%B0%E4%BC%A0%E9%80%92"><span class="toc-number">2.2.2.</span> <span class="toc-text">动态参数传递</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E6%89%98%E7%AE%A1"><span class="toc-number">2.2.3.</span> <span class="toc-text">静态资源托管</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%B7%AF%E7%94%B1"><span class="toc-number">2.3.</span> <span class="toc-text">路由</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%B7%AF%E7%94%B1%E6%A8%A1%E5%9D%97%E5%8C%96"><span class="toc-number">2.3.1.</span> <span class="toc-text">路由模块化</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%AD%E9%97%B4%E4%BB%B6"><span class="toc-number">2.4.</span> <span class="toc-text">中间件</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%86%E7%B1%BB"><span class="toc-number">2.4.1.</span> <span class="toc-text">分类</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%86%85%E7%BD%AE%E4%B8%AD%E9%97%B4%E4%BB%B6"><span class="toc-number">2.4.2.</span> <span class="toc-text">内置中间件</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%AC%AC%E4%B8%89%E6%96%B9%E4%B8%AD%E9%97%B4%E4%BB%B6"><span class="toc-number">2.4.3.</span> <span class="toc-text">第三方中间件</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%87%AA%E5%AE%9A%E4%B9%89%E4%B8%AD%E9%97%B4%E4%BB%B6"><span class="toc-number">2.4.4.</span> <span class="toc-text">自定义中间件</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BC%82%E5%B8%B8%E4%B8%AD%E9%97%B4%E4%BB%B6"><span class="toc-number">2.4.5.</span> <span class="toc-text">异常中间件</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#404%E4%B8%AD%E9%97%B4%E4%BB%B6"><span class="toc-number">2.4.6.</span> <span class="toc-text">404中间件</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#cookie"><span class="toc-number">2.5.</span> <span class="toc-text">cookie</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#session"><span class="toc-number">2.6.</span> <span class="toc-text">session</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%9E%E6%8E%A5%E6%95%B0%E6%8D%AE%E5%BA%93"><span class="toc-number">2.7.</span> <span class="toc-text">连接数据库</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0"><span class="toc-number">2.8.</span> <span class="toc-text">文件上传</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%8E%A5%E5%8F%A3%E8%AE%BE%E8%AE%A1"><span class="toc-number">2.9.</span> <span class="toc-text">接口设计</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#jwt"><span class="toc-number">2.9.1.</span> <span class="toc-text">jwt</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#koa2"><span class="toc-number">3.</span> <span class="toc-text">koa2</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%AE%80%E5%8D%95%E4%BB%8B%E7%BB%8D-2"><span class="toc-number">3.1.</span> <span class="toc-text">简单介绍</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9D%E5%A7%8B%E5%8C%96%E9%A1%B9%E7%9B%AE-1"><span class="toc-number">3.1.1.</span> <span class="toc-text">初始化项目</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%B7%AF%E7%94%B1-1"><span class="toc-number">3.2.</span> <span class="toc-text">路由</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%8E%B7%E5%8F%96%E5%8F%82%E6%95%B0"><span class="toc-number">3.2.1.</span> <span class="toc-text">获取参数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8A%A8%E6%80%81%E8%B7%AF%E7%94%B1"><span class="toc-number">3.2.2.</span> <span class="toc-text">动态路由</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%A8%A1%E5%9D%97%E5%8C%96"><span class="toc-number">3.2.3.</span> <span class="toc-text">模块化</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%AD%E9%97%B4%E4%BB%B6-1"><span class="toc-number">3.3.</span> <span class="toc-text">中间件</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%B7%AF%E7%94%B1%E4%B8%AD%E9%97%B4%E4%BB%B6"><span class="toc-number">3.3.1.</span> <span class="toc-text">路由中间件</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86%E4%B8%AD%E9%97%B4%E4%BB%B6"><span class="toc-number">3.3.2.</span> <span class="toc-text">错误处理中间件</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#post%E6%8F%90%E4%BA%A4%E6%95%B0%E6%8D%AE"><span class="toc-number">3.4.</span> <span class="toc-text">post提交数据</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90"><span class="toc-number">3.5.</span> <span class="toc-text">静态资源</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%8A%E4%BC%A0%E6%96%87%E4%BB%B6"><span class="toc-number">3.6.</span> <span class="toc-text">上传文件</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#koa-body-%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%8F%82%E6%95%B0"><span class="toc-number">3.6.1.</span> <span class="toc-text">koa-body 的基本参数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#formidable-%E7%9A%84%E7%9B%B8%E5%85%B3%E9%85%8D%E7%BD%AE%E5%8F%82%E6%95%B0"><span class="toc-number">3.6.2.</span> <span class="toc-text">formidable 的相关配置参数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8F%A6%E4%B8%80%E7%A7%8D%E9%85%8D%E7%BD%AE"><span class="toc-number">3.6.3.</span> <span class="toc-text">另一种配置</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%B7%A8%E5%9F%9F"><span class="toc-number">3.7.</span> <span class="toc-text">跨域</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#cookie-1"><span class="toc-number">3.8.</span> <span class="toc-text">cookie</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#session-1"><span class="toc-number">3.9.</span> <span class="toc-text">session</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%95%B0%E6%8D%AE%E5%BA%93"><span class="toc-number">3.10.</span> <span class="toc-text">数据库</span></a></li></ol></li></ol></div></div><div class="author-info hide"><div class="author-info__avatar text-center"><img src="/img/avatar.png"></div><div class="author-info__name text-center">晟松</div><div class="author-info__description text-center">晟松的博客</div><div class="follow-button"><a target="_blank" rel="noopener" href="https://gitee.com/cheng_song">Gitee</a></div><hr><div class="author-info-articles"><a class="author-info-articles__archives article-meta" href="/archives"><span class="pull-left">文章</span><span class="pull-right">52</span></a><a class="author-info-articles__tags article-meta" href="/tags"><span class="pull-left">标签</span><span class="pull-right">45</span></a></div></div></div><div id="content-outer"><div id="top-container" style="background-image: url(/img/blog-bg.jpg)"><div id="page-header"><span class="pull-left"> <a id="site-name" href="/">晟松</a></span><i class="fa fa-bars toggle-menu pull-right" aria-hidden="true"></i><span class="pull-right menus">   <a class="site-page" href="/">Home</a><a class="site-page" href="/archives">Archives</a><a class="site-page" href="/tags">Tags</a><a class="site-page" href="/categories">Categories</a></span><span class="pull-right"><a class="site-page social-icon search"><i class="fa fa-search"></i><span> 搜索</span></a></span></div><div id="post-info"><div id="post-title">Nodejs express koa2初学习</div><div id="post-meta"><time class="post-meta__date"><i class="fa fa-calendar" aria-hidden="true"></i> 2021-02-05</time><div class="post-meta-wordcount"><span>字数总计: </span><span class="word-count">9k</span><span class="post-meta__separator">|</span><span>阅读时长: 36 分钟</span></div></div></div></div><div class="layout" id="content-inner"><article id="post"><div class="article-container" id="post-content"><p>这个文章主要记录一下对nodejs的一个框架express的学习，虽然其实node目前有很多框架，但目前我主要的是在学习前端，后端目前只是写一些简单的接口，为前端进行使用，目前想要学习的框架有两个，一个是目前的express，下一步应该是koa2，为什么目前要先学习express呐，主要是express的社区比较完善，比较完善的能找到一些学习资料和视频，而koa看着好像社区还不太繁荣，所以目前先学着express，等学完之后，再去学习koa2</p>
<p><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1Hz4y1U7i7?from=search&seid=5221341051997279262">node+express教程</a></p>
<p><a target="_blank" rel="noopener" href="https://www.jspang.com/detailed?id=34">koa2教程1</a></p>
<p><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV11t411k79h?p=43">koa2教程2</a></p>
<h2 id="node"><a href="#node" class="headerlink" title="node"></a>node</h2><h3 id="简单介绍"><a href="#简单介绍" class="headerlink" title="简单介绍"></a>简单介绍</h3><p>跟着上面的node——express的视频线简单学一些node常用的重要内容，然后看一下express的东西</p>
<p><strong>模块化</strong> ：nodejs基于commonjs模块化开发的规范，其定义一个js文件就是一个模块</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 导出</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;func_name&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 导入</span></span><br><span class="line"><span class="keyword">let</span> name = <span class="built_in">require</span>(<span class="string">&quot;module_name&quot;</span>)</span><br></pre></td></tr></table></figure>

<p>node的模块类型:</p>
<ul>
<li>核心(内置）模块-安装nodejs自带的模块</li>
<li>第三方模块–需要手动通过(npm l yarn)来进行安装</li>
<li>自定义模块-开发者自己编写开发</li>
</ul>
<p><strong>执行</strong></p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">node name.js</span><br></pre></td></tr></table></figure>

<p>注意一下文件路径</p>
<p><strong>全局变量</strong></p>
<p>常用的有这下面3个，可以直接在node环境中进行使用</p>
<ul>
<li>global:全局变量的宿主(类似于浏览器js中的window对象)，这是一个特殊的对象，称为全局对象(Global Object)，它及其所有属性都可以在程序的任何地方访问</li>
<li>__filename:当前正在执行的脚本的文件名(完整路径)</li>
<li>__dirname:当前执行脚本所在的目录绝对路径</li>
</ul>
<p>还有一个<a target="_blank" rel="noopener" href="http://nodejs.cn/api/process.html#process_process_env">process</a>的全局变量</p>
<p>特别是里面的 <strong>process.env</strong> 属性，会返回包含用户环境的对象。常在vue的js文件里面出现进行一些判断</p>
<h3 id="常用内置模块"><a href="#常用内置模块" class="headerlink" title="常用内置模块"></a>常用内置模块</h3><p><a target="_blank" rel="noopener" href="http://nodejs.cn/api/">官网api介绍</a></p>
<h4 id="os模块"><a href="#os模块" class="headerlink" title="os模块"></a>os模块</h4><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> os = <span class="built_in">require</span>(<span class="string">&#x27;os&#x27;</span>);</span><br><span class="line"></span><br><span class="line">os.EOL <span class="comment">// 根据操作系统特定的行末标志。在 LINUX 上是 \n。在 Windows 上是 \r\n。</span></span><br><span class="line">os.cpus() <span class="comment">// 查看cpu相关信息</span></span><br><span class="line">os.hostname() <span class="comment">// 以字符串的形式返回操作系统的主机名。</span></span><br><span class="line">os.totalmem() <span class="comment">// 查看总内存</span></span><br><span class="line">os.freemem() <span class="comment">// 查看空闲内存</span></span><br><span class="line">os.type() <span class="comment">// 返回一样的操作系统名字。 例如，在 Linux 上返回 &#x27;Linux&#x27;，在 macOS 上返回 &#x27;Darwin&#x27;，在 Windows 上返回 &#x27;Windows_NT&#x27;。</span></span><br><span class="line"></span><br><span class="line">....</span><br></pre></td></tr></table></figure>

<p>ELO     end of line</p>
<p>ELF      end of file</p>
<h4 id="path模块"><a href="#path模块" class="headerlink" title="path模块"></a>path模块</h4><p>用于处理文件和目录的路径</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">&#x27;path&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//获取路径最后一部内容，一般用它来获取文件名称</span></span><br><span class="line">path.basename( <span class="string">&#x27;c:/a/b/c/d.htm1 &#x27;</span>)l/ d.html</span><br><span class="line"><span class="comment">//获取目录名，路径最后分隔符部分被忽略</span></span><br><span class="line">path.dirname(<span class="string">&#x27;c:/a/b/c/d.html&#x27;</span>) <span class="comment">// c:/a/b/c</span></span><br><span class="line"><span class="comment">//获取路径中文件扩展名</span></span><br><span class="line">path.extname(<span class="string">&#x27;c:/a/b/c/d.htm1 &#x27;</span>) <span class="comment">// .htm1</span></span><br><span class="line"><span class="comment">//给定的路径连接在一起</span></span><br><span class="line">path.join(<span class="string">&#x27;/a&#x27;</span>, <span class="string">&#x27;b&#x27;</span>, <span class="string">&#x27;c&#x27;</span>) <span class="comment">// /a/b/c </span></span><br><span class="line"><span class="comment">// resolve:模拟cd（切换目录）操作的同时拼接路径,按照参数顺序执行(正反斜杠由node根据系统自动判断)</span></span><br><span class="line"><span class="built_in">console</span>.log(path.resolve(<span class="string">&quot;a&quot;</span>，<span class="string">&quot;b&quot;</span>，<span class="string">&quot;c&quot;</span>)); <span class="comment">// a/b/c</span></span><br><span class="line"><span class="built_in">console</span>.1og(path.reso1ve(<span class="string">&quot;a&quot;</span>，<span class="string">&quot;../b&quot;</span>，<span class="string">&quot;c&quot;</span>)); <span class="comment">// b/c</span></span><br><span class="line">conso1e.1og(path.resolve(<span class="string">&quot;/a&quot;</span>，<span class="string">&quot;b&quot;</span>，<span class="string">&quot;c&quot;</span>)); <span class="comment">// c:/a/b/c</span></span><br><span class="line">……</span><br></pre></td></tr></table></figure>

<h4 id="url模块"><a href="#url模块" class="headerlink" title="url模块"></a>url模块</h4><p><code>url</code> 模块用于处理与解析 URL</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">┌────────────────────────────────────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                                              href                                              │</span><br><span class="line">├──────────┬──┬─────────────────────┬────────────────────────┬───────────────────────────┬───────┤</span><br><span class="line">│ protocol │  │        auth         │          host          │           path            │ hash  │</span><br><span class="line">│          │  │                     ├─────────────────┬──────┼──────────┬────────────────┤       │</span><br><span class="line">│          │  │                     │    hostname     │ port │ pathname │     search     │       │</span><br><span class="line">│          │  │                     │                 │      │          ├─┬──────────────┤       │</span><br><span class="line">│          │  │                     │                 │      │          │ │    query     │       │</span><br><span class="line">&quot;  https:   //    user   :   pass   @ sub.example.com : 8080   /p/a/t/h  ?  query=string   #hash &quot;</span><br><span class="line">│          │  │          │          │    hostname     │ port │          │                │       │</span><br><span class="line">│          │  │          │          ├─────────────────┴──────┤          │                │       │</span><br><span class="line">│ protocol │  │ username │ password │          host          │          │                │       │</span><br><span class="line">├──────────┴──┼──────────┴──────────┼────────────────────────┤          │                │       │</span><br><span class="line">│   origin    │                     │         origin         │ pathname │     search     │ hash  │</span><br><span class="line">├─────────────┴─────────────────────┴────────────────────────┴──────────┴────────────────┴───────┤</span><br><span class="line">│                                              href                                              │</span><br><span class="line">└────────────────────────────────────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure>



<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> url = <span class="built_in">require</span>(<span class="string">&#x27;url&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> href = <span class="string">&#x27;http://api.chengsong.info?id=1&#x27;</span></span><br><span class="line"><span class="keyword">let</span> obj = <span class="keyword">new</span> URL(href)</span><br><span class="line">url.parse(href,<span class="literal">true</span>) <span class="comment">// 解析网址，返回url对象，所以可以直接new一个url对象，后面的true是node自动将后面的query参数转换成对象的形式，方便拿到，如果没有这个true的话就是一个字符串</span></span><br><span class="line"><span class="comment">// 结果</span></span><br><span class="line">Url &#123;</span><br><span class="line">  <span class="attr">protocol</span>: <span class="string">&#x27;http:&#x27;</span>,</span><br><span class="line">  <span class="attr">slashes</span>: <span class="literal">true</span>,</span><br><span class="line">  <span class="attr">auth</span>: <span class="literal">null</span>,</span><br><span class="line">  <span class="attr">host</span>: <span class="string">&#x27;api.chengsong.info&#x27;</span>,</span><br><span class="line">  <span class="attr">port</span>: <span class="literal">null</span>,</span><br><span class="line">  <span class="attr">hostname</span>: <span class="string">&#x27;api.chengsong.info&#x27;</span>,</span><br><span class="line">  <span class="attr">hash</span>: <span class="literal">null</span>,</span><br><span class="line">  <span class="attr">search</span>: <span class="string">&#x27;?id=1&#x27;</span>,</span><br><span class="line">  <span class="attr">query</span>: [<span class="built_in">Object</span>: <span class="literal">null</span> prototype] &#123; <span class="attr">id</span>: <span class="string">&#x27;1&#x27;</span> &#125;,</span><br><span class="line">  <span class="attr">pathname</span>: <span class="string">&#x27;/&#x27;</span>,</span><br><span class="line">  <span class="attr">path</span>: <span class="string">&#x27;/&#x27;</span>,</span><br><span class="line">  <span class="attr">href</span>: <span class="string">&#x27;http://api.chengsong.info/&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//将多个地址拼接成一个新的url，还是要注意路径规则,前面的根目录是在端口号后面的那一个斜杠</span></span><br><span class="line">ur1.resolve( href , <span class="string">&#x27;bar&#x27;</span>) <span class="comment">// http://api.chengsong.info/bar</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h4 id="querystring模块"><a href="#querystring模块" class="headerlink" title="querystring模块"></a>querystring模块</h4><p><code>querystring</code> 模块提供用于解析和格式化 URL 查询字符串的实用工具。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> querystring = <span class="built_in">require</span>(<span class="string">&#x27;querystring&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// query字符串转换为对象</span></span><br><span class="line">querystring.parse(<span class="string">&#x27;id=1&amp;name=cs&#x27;</span>)</span><br><span class="line">querystring.decode(<span class="string">&#x27;id=1&amp;name=cs&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 对象转为query字符串,注意下面的 1 是上面会转成字符串</span></span><br><span class="line">querystring.stringify(&#123;<span class="attr">id</span>:<span class="string">&#x27;1&#x27;</span>,<span class="attr">name</span>:<span class="string">&#x27;cs&#x27;</span>&#125;)</span><br><span class="line">querystring.encode(&#123;<span class="attr">id</span>:<span class="string">&#x27;1&#x27;</span>,<span class="attr">name</span>:<span class="string">&#x27;cs&#x27;</span>&#125;)</span><br></pre></td></tr></table></figure>



<h4 id="fs模块"><a href="#fs模块" class="headerlink" title="fs模块"></a>fs模块</h4><p>对文件进行操作，提供了同步和异步两种对文件的操作</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">&#x27;fs&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 注意读取的数据是Buffer类型的，需要一些转换，或者直接指定读取的类型 如 utf-8</span></span><br><span class="line"><span class="comment">// 异步读取</span></span><br><span class="line">fs.readFile(<span class="string">&#x27;input.txt&#x27;</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err, data</span>) </span>&#123;</span><br><span class="line">   <span class="keyword">if</span> (err) &#123;</span><br><span class="line">       <span class="keyword">return</span> <span class="built_in">console</span>.error(err);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="built_in">console</span>.log(<span class="string">&quot;异步读取: &quot;</span> + data.toString());</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 同步读取</span></span><br><span class="line"><span class="keyword">var</span> data = fs.readFileSync(<span class="string">&#x27;input.txt&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 写入数据（覆盖） 追加写 fs.appendFile	如果文件不存在会自动创建</span></span><br><span class="line">fs.writeFileSync(<span class="string">&#x27;a.txt&#x27;</span>, <span class="string">&#x27;要写入的数据&#x27;</span>,<span class="function"><span class="params">err</span>=&gt;</span>&#123;&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检测文件是否存在</span></span><br><span class="line"><span class="keyword">let</span> res = fs.existsSync(path)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 删除文件</span></span><br><span class="line">fs.unlink(filename,<span class="function"><span class="params">err</span>=&gt;</span>&#123;&#125;)</span><br></pre></td></tr></table></figure>

<p><strong>获取文件信息</strong></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">fs.stat(<span class="string">&#x27;a.txt&#x27;</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err, stats</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(stats.isFile());         <span class="comment">//true</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<table>
<thead>
<tr>
<th align="left">方法</th>
<th align="left">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="left">stats.isFile()</td>
<td align="left">如果是文件返回 true，否则返回 false。</td>
</tr>
<tr>
<td align="left">stats.isDirectory()</td>
<td align="left">如果是目录返回 true，否则返回 false。</td>
</tr>
<tr>
<td align="left">stats.isBlockDevice()</td>
<td align="left">如果是块设备返回 true，否则返回 false。</td>
</tr>
<tr>
<td align="left">stats.isCharacterDevice()</td>
<td align="left">如果是字符设备返回 true，否则返回 false。</td>
</tr>
<tr>
<td align="left">stats.isSymbolicLink()</td>
<td align="left">如果是软链接返回 true，否则返回 false。</td>
</tr>
<tr>
<td align="left">stats.isFIFO()</td>
<td align="left">如果是FIFO，返回true，否则返回 false。FIFO是UNIX中的一种特殊类型的命令管道。</td>
</tr>
<tr>
<td align="left">stats.isSocket()</td>
<td align="left">如果是 Socket 返回 true，否则返回 false。</td>
</tr>
</tbody></table>
<h4 id="http模块"><a href="#http模块" class="headerlink" title="http模块"></a>http模块</h4><p>这个模块就是用来创建一个web服务器</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">&#x27;http&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> server = http.createServer()</span><br><span class="line"></span><br><span class="line">server.on(<span class="string">&#x27;request&#x27;</span>,<span class="function">(<span class="params">req,res</span>)=&gt;</span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (req.url == <span class="string">&#x27;/cs&#x27;</span>) &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;cs&#x27;</span>)</span><br><span class="line">        res.end(<span class="string">&#x27;蒋晟松&#x27;</span>) <span class="comment">// express框架提供了 res.send(&#x27;data&#x27;) 这个api</span></span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (req.url == <span class="string">&#x27;/&#x27;</span>) &#123;</span><br><span class="line">        res.end(<span class="string">&#x27;123&#x27;</span>)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        res.end(<span class="string">&#x27;error&#x27;</span>)</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 这里的req就是客户端请求对象，包含url，query这些的东西</span></span><br><span class="line">    <span class="comment">// 这里的res就是服务器对客户端的响应对象，</span></span><br><span class="line">    <span class="comment">// 设置响应头信息，用于解决响应时中文乱码的问题	res.setHeader(&#x27;Content-Type&#x27;,&#x27;text/html;charset=utf-8&#x27;)</span></span><br><span class="line">    <span class="comment">// 设置状态码	res.statusCode = 200</span></span><br><span class="line">    <span class="comment">// 发送数据 res.end()</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// 启动服务，需要给一个端口</span></span><br><span class="line">server.listen(<span class="number">8000</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;服务启动&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>res.end() 传递的数据只能是字符串，res.send() 可传递对象这些，express会自动将其转为json返回</p>
<h5 id="静态资源服务器"><a href="#静态资源服务器" class="headerlink" title="静态资源服务器"></a>静态资源服务器</h5><p>客户端请求的每个资源uri地址，作为在本机服务器指定目录中的文件。通过相关模块进行读取文件中数据进行响应给客户端，从而实现静态服务器。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">server.on(<span class="string">&#x27;request&#x27;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">    res.statusCode = <span class="number">200</span></span><br><span class="line">    <span class="keyword">var</span> pathname = url.parse(req.url).pathname</span><br><span class="line">    <span class="comment">//真的读取这个文件</span></span><br><span class="line">    fs.readFile(<span class="string">&#x27;../public&#x27;</span> + pathname, <span class="function"><span class="keyword">function</span> (<span class="params">err, data</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (err) &#123;</span><br><span class="line">            <span class="comment">// 注意这个header的设置不能设置到图片数据上面，会出现格式错误</span></span><br><span class="line">            res.setHeader(<span class="string">&#x27;Content-Type&#x27;</span>, <span class="string">&#x27;text/html;charset=utf-8&#x27;</span>)</span><br><span class="line">            res.end(<span class="string">&#x27;不存在&#x27;</span>)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        &#125;</span><br><span class="line">        res.end(data)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p>默认情况下，浏览器访问网站的时候会请求一个 /favicon.ico 的图标文件，这就是你的网页的标签里面的小图标，可直接将你的图标放在public目录下面</p>
<h5 id="get"><a href="#get" class="headerlink" title="get"></a>get</h5><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// get</span></span><br><span class="line"><span class="comment">// 主要是使用 query 这个属性</span></span><br><span class="line"><span class="keyword">var</span> &#123;query&#125; = url.parse(req.url, <span class="literal">true</span>)</span><br></pre></td></tr></table></figure>

<h5 id="post"><a href="#post" class="headerlink" title="post"></a>post</h5><p>表单数据多数为post进行提交到服务器端。需要监听req对象的data事件来获取客户端发送到服务器的数据。如果数据量比较大，无法一次性发送完毕，则客户端会把数据切割后分批次发送给服务器。所以data事件可能会被触发多次，每次触发data事件时，收到的数据只是全部数据的一部分，因此需要做数据的拼接才能得到完整的数据</p>
<p><strong>特别需要</strong>：注意传来的请求头的格式 是formdata，还是applicationlx-www-form-urlencoded等等</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">server.on(<span class="string">&#x27;request&#x27;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line"><span class="keyword">var</span> body = <span class="string">&quot;&quot;</span>;</span><br><span class="line">  req.on(<span class="string">&#x27;data&#x27;</span>, <span class="function"><span class="keyword">function</span> (<span class="params">chunk</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 拼接所有数据</span></span><br><span class="line">    body += chunk;</span><br><span class="line">  &#125;);</span><br><span class="line">  req.on(<span class="string">&#x27;end&#x27;</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 解析参数</span></span><br><span class="line">    body = querystring.parse(body);</span><br><span class="line">    <span class="comment">// 设置响应头部信息及编码</span></span><br><span class="line">    res.writeHead(<span class="number">200</span>, &#123;<span class="string">&#x27;Content-Type&#x27;</span>: <span class="string">&#x27;text/html; charset=utf8&#x27;</span>&#125;);</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">if</span>(body.name &amp;&amp; body.url) &#123; <span class="comment">// 输出提交的数据</span></span><br><span class="line">        res.write(<span class="string">&quot;网站名：&quot;</span> + body.name);</span><br><span class="line">        res.write(<span class="string">&quot;&lt;br&gt;&quot;</span>);</span><br><span class="line">        res.write(<span class="string">&quot;网站 URL：&quot;</span> + body.url);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;  <span class="comment">// 输出表单</span></span><br><span class="line">        res.write(postHTML);</span><br><span class="line">    &#125;</span><br><span class="line">    res.end();</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="注意点"><a href="#注意点" class="headerlink" title="注意点"></a>注意点</h3><p>nodejs是js的后端运行环境，所以无法使用浏览器的DOM和BOM这些内置api</p>
<h2 id="express"><a href="#express" class="headerlink" title="express"></a>express</h2><h3 id="简单介绍-1"><a href="#简单介绍-1" class="headerlink" title="简单介绍"></a>简单介绍</h3><h4 id="初始化项目"><a href="#初始化项目" class="headerlink" title="初始化项目"></a>初始化项目</h4><p>使用express开发的时候需要先在本地安装这个环境</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install express -g</span><br><span class="line">npm insatll -g express-generator <span class="comment"># 这一步是4.x版本的问题</span></span><br><span class="line"></span><br><span class="line">express --version <span class="comment"># 检测是否安装好了</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建项目</span></span><br><span class="line">express projectName <span class="comment"># 使用express框架自动创建项目</span></span><br><span class="line"><span class="built_in">cd</span> projectName</span><br><span class="line">npm install <span class="comment"># 安装package.json里面的依赖</span></span><br><span class="line">npm start <span class="comment"># 运行项目 实际上是运行 bin目录里面的 www 文件里的http模块</span></span><br></pre></td></tr></table></figure>

<p>其实也可以将bin目录里面的www文件里的内容直接复制到<strong>app.js</strong>文件里面（主要是里面关于 http模块 的一些代码），然后使用</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">nodemon app.js <span class="comment"># 可能需要安装nodemon</span></span><br></pre></td></tr></table></figure>

<p>来方便开发（因为nodemon会自动根据你的更新重新运行项目，npm satrt 试了下没有这个功能） </p>
<h3 id="创建web服务"><a href="#创建web服务" class="headerlink" title="创建web服务"></a>创建web服务</h3><p>步骤：导入需要使用的包；创建一个web实例；定义运行访问的路由；启动服务</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> express = <span class="built_in">require</span>(<span class="string">&#x27;express&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> app = express()</span><br><span class="line"><span class="comment">// 注意这个路径是可以重复的，但极其不推荐</span></span><br><span class="line">app.get(<span class="string">&#x27;/&#x27;</span>,<span class="function">(<span class="params">req,res</span>)=&gt;</span>&#123;</span><br><span class="line">    <span class="comment">// 这里可直接传入对象</span></span><br><span class="line">    res.send(&#123;<span class="attr">id</span>:<span class="number">1</span>,<span class="attr">name</span>:<span class="string">&#x27;蒋晟松&#x27;</span>&#125;)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 对页面 abcd, abxcd, ab123cd, 等响应 GET 请求</span></span><br><span class="line">app.get(<span class="string">&#x27;/ab*cd&#x27;</span>, <span class="function"><span class="keyword">function</span>(<span class="params">req, res</span>) </span>&#123;   </span><br><span class="line">   <span class="built_in">console</span>.log(<span class="string">&quot;/ab*cd GET 请求&quot;</span>);</span><br><span class="line">   res.send(<span class="string">&#x27;正则匹配&#x27;</span>);</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">app.post(<span class="string">&#x27;/post&#x27;</span>,<span class="function">(<span class="params">req,res</span>)=&gt;</span>&#123;</span><br><span class="line">    </span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// 其他的put，delete这些都差不多</span></span><br><span class="line"></span><br><span class="line">app.listen(<span class="number">8000</span>,<span class="function">()=&gt;</span>&#123;&#125;)</span><br></pre></td></tr></table></figure>

<p>访问地址的匹配规则，代码从上往下执行，只有请求类型和请求路径都匹配上，才会执行对于回调函数</p>
<h4 id="获取query"><a href="#获取query" class="headerlink" title="获取query"></a>获取query</h4><p>直接通过 <strong>req,query</strong> 这个对象就可获取url里面的query数据</p>
<h4 id="动态参数传递"><a href="#动态参数传递" class="headerlink" title="动态参数传递"></a>动态参数传递</h4><p>express也支持vue中 <strong>动态路由</strong> 的形式传递参数，传递的参数通过req.params对象获取到</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 必须的路由参数，不传就匹配不上</span></span><br><span class="line">app.get(<span class="string">&#x27;/:id&#x27;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(req.params) <span class="comment">// http://localhost:8000/2	 &#123; id: &#x27;2&#x27; &#125;</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">// 可选的路由参数，传不传都行	</span></span><br><span class="line">app.get(<span class="string">&#x27;/:id?&#x27;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(req.params)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h4 id="静态资源托管"><a href="#静态资源托管" class="headerlink" title="静态资源托管"></a>静态资源托管</h4><p>Express 提供了内置的中间件 <strong>express.static</strong> 来设置静态文件如：图片， CSS, JavaScript 等。</p>
<p>你可以使用 <strong>express.static</strong> 中间件来设置静态文件路径。例如，如果你将图片， CSS, JavaScript 文件放在 public 目录下，你可以这么写：</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">app.use(&#x27;/images&#x27;, express.static(&#x27;public/img&#x27;));</span><br></pre></td></tr></table></figure>

<p>前面的 <strong>/images</strong> 是匹配的路径（或称虚拟前缀），后面的public是对应的目录</p>
<p>访问：<a target="_blank" rel="noopener" href="http://localhost:8000/images/1.jpg">http://localhost:8000/images/1.jpg</a>     得到 public/img目录下面的1.jpg文件</p>
<p>app.use()    一般写在监听端口之前</p>
<h3 id="路由"><a href="#路由" class="headerlink" title="路由"></a>路由</h3><p>路由的形式就跟上面的形式差不多</p>
<h4 id="路由模块化"><a href="#路由模块化" class="headerlink" title="路由模块化"></a><a target="_blank" rel="noopener" href="https://gitee.com/cheng_song/node-express">路由模块化</a></h4><p>在使用express创建的项目里面的routes目录里面的就是分开的各种路由并导出</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">&#x27;express&#x27;</span>);</span><br><span class="line"><span class="comment">// const dbconfig = require(&#x27;../util/dbconfig&#x27;);</span></span><br><span class="line"><span class="keyword">var</span> router = express.Router();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这里引入了回调函数</span></span><br><span class="line"><span class="keyword">const</span> cate = <span class="built_in">require</span>(<span class="string">&#x27;../controllers/cateController&#x27;</span>)</span><br><span class="line"><span class="comment">/* GET home page. */</span></span><br><span class="line"><span class="comment">// 对相应路由进行数据请求，并返回,将对应的方法封装到了其他的js文件里面</span></span><br><span class="line">router.get(<span class="string">&#x27;/&#x27;</span>, cate.getcate);</span><br><span class="line">router.get(<span class="string">&#x27;/post&#x27;</span>,cate.getPostCate);</span><br><span class="line"><span class="built_in">module</span>.exports = router;</span><br></pre></td></tr></table></figure>

<p>然后在app.js里面require，然后使用</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 引入路由文件</span></span><br><span class="line"><span class="keyword">var</span> indexRouter = <span class="built_in">require</span>(<span class="string">&#x27;./routes/index&#x27;</span>)</span><br><span class="line"><span class="keyword">var</span> usersRouter = <span class="built_in">require</span>(<span class="string">&#x27;./routes/users&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 这里也支持前缀</span></span><br><span class="line">app.use(<span class="string">&#x27;/&#x27;</span>, indexRouter)</span><br><span class="line">app.use(<span class="string">&#x27;/users&#x27;</span>, usersRouter)</span><br></pre></td></tr></table></figure>

<h3 id="中间件"><a href="#中间件" class="headerlink" title="中间件"></a>中间件</h3><p>express中，当一个请求到达的服务器之后，可以在给客户响应之前连续调用<strong>多个</strong>中间件，来对本次请求和返回响应数据进行处理。</p>
<h4 id="分类"><a href="#分类" class="headerlink" title="分类"></a>分类</h4><ul>
<li>内置中间件    无需npm安装 如 express.static()</li>
<li>第三方中间件 通过npm安装 如 body-parser</li>
<li>自定义中间件</li>
</ul>
<p>也可分为：</p>
<ul>
<li>应用中间件<ul>
<li>全局使用 app.use()    所有路由都生效</li>
<li>局部使用 app.请求方法(地址,[中间件…],回调函数)  当前路由生效</li>
</ul>
</li>
<li>路由级别中间件,只是绑定在router上<ul>
<li>router.use(中间件)</li>
<li>router.请求方法(地址,[中间件…],回调函数)</li>
</ul>
</li>
</ul>
<h4 id="内置中间件"><a href="#内置中间件" class="headerlink" title="内置中间件"></a>内置中间件</h4><p><strong>express.static()</strong>    静态资源管理</p>
<p><strong>express.json</strong>    接收json格式提交的数据</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">app.use(express.json())</span><br></pre></td></tr></table></figure>

<p>其在接收完数据之后，会将数据的对象形式挂载到请求对象的body属性上</p>
<p><strong>express.urlencoded</strong>    处理post表单提交的数据</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">app.use(express.urlencoded(&#123; extended: false &#125;))</span><br></pre></td></tr></table></figure>

<p>其在接收完数据之后，也会将数据的对象形式挂载到请求对象的body属性上</p>
<p>上面两个需要的express版本需要&gt;=4.16.0    所以会有一些兼容性的问题</p>
<p>上面两个不会出现覆盖的问题，因为传递的数据只会是一种格式</p>
<ul>
<li><code>extended: false</code>：表示使用系统模块querystring来处理，会剔除传递过来的方法和对象，也是官方推荐的</li>
<li><code>extended: true</code>：表示使用第三方模块qs来处理</li>
</ul>
<hr>
<h4 id="第三方中间件"><a href="#第三方中间件" class="headerlink" title="第三方中间件"></a>第三方中间件</h4><p>以body-parser为例</p>
<p>使用前需要安装</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">npm install -S body-parser</span><br></pre></td></tr></table></figure>

<p>然后在要使用的文件里面引入，并使用app.use()调用，然后就行了</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> bodyParser = <span class="built_in">require</span>(<span class="string">&#x27;body-parser&#x27;</span>)</span><br><span class="line">app.use(bodyParser.urlencoded(&#123; <span class="attr">extended</span>: <span class="literal">true</span> &#125;))</span><br><span class="line">app.use(bodyParser.json())</span><br></pre></td></tr></table></figure>

<hr>
<h4 id="自定义中间件"><a href="#自定义中间件" class="headerlink" title="自定义中间件"></a>自定义中间件</h4><p>自定义中间件，其本质就是定义一个处理请求的函数，只是此函数中除了有request和response参数外还必须包含一个next参数，此参数作用让中间件能够让流程向下执行下去直到匹配到的路由中发送响应给客户端。也可以通过给request对象添加属性来进行中间件数据的向下传递</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">myfn</span>(<span class="params">req,res,next</span>)</span>&#123;</span><br><span class="line">	……</span><br><span class="line">	next()	<span class="comment">// 中间件最后一句必须执行此语句，进行放行</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用</span></span><br><span class="line">app.use(myfn)</span><br></pre></td></tr></table></figure>

<p>在整个请求链路中，所有中间件与最终路由共用一份req和res</p>
<p>注意上面的next() 有可能与异步代码发生冲突，所以注意位置</p>
<h4 id="异常中间件"><a href="#异常中间件" class="headerlink" title="异常中间件"></a>异常中间件</h4><p>作用:专门用来捕获整个项目发生的异常错误，从而防止项目异常崩溃的问题产生(友好显示异常)。</p>
<p>格式:错误级别中间件的函数参数中，必须有四个形参，分别是(err,req,res , next)</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.get(<span class="string">&#x27;/err&#x27;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">&#x27;发生错误&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">app.use(<span class="function">(<span class="params">err, req, res, next</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;err:&#x27;</span>, err)</span><br><span class="line">    res.send(<span class="string">&#x27;error:&#x27;</span> + err.message) <span class="comment">// error:发生错误</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h4 id="404中间件"><a href="#404中间件" class="headerlink" title="404中间件"></a>404中间件</h4><p>如果所有的路由都匹配不上，即转到这个页面</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">app.use((req, res, next) =&gt; &#123;</span><br><span class="line">    res.status(404).send(&#x27;404&#x27;)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p>上面两个中间件的app.use() 都需要放在代码最下面，不然会使代码产生错误</p>
<h3 id="cookie"><a href="#cookie" class="headerlink" title="cookie"></a>cookie</h3><p>使用 <strong>cookie-parser</strong> 这个中间件向 Node.js 服务器发送 cookie 信息，需要先安装</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> cookieParser = <span class="built_in">require</span>(<span class="string">&#x27;cookie-parser&#x27;</span>)</span><br><span class="line"><span class="keyword">var</span> util = <span class="built_in">require</span>(<span class="string">&#x27;util&#x27;</span>);</span><br><span class="line"> </span><br><span class="line"><span class="keyword">var</span> app = express()</span><br><span class="line">app.use(cookieParser())</span><br><span class="line"> </span><br><span class="line">app.get(<span class="string">&#x27;/&#x27;</span>, <span class="function"><span class="keyword">function</span>(<span class="params">req, res</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 服务端通过req获取cookie信息</span></span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&quot;Cookies: &quot;</span> + util.inspect(req.cookies));</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 设置cookie，并包括过期时间。如果没有设置时间，则默认在会话期间有效</span></span><br><span class="line">    res.cookie(<span class="string">&#x27;username&#x27;</span>,<span class="string">&#x27;admin&#x27;</span>,&#123;<span class="attr">maxAge</span>:<span class="number">86400</span>*<span class="number">1000</span>&#125;)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h3 id="session"><a href="#session" class="headerlink" title="session"></a>session</h3><p>因为cookie是存放在客户端里的，所以会有被修改的风险，所以需要存放在服务端的session，然后在cookie里面存一个session的id</p>
<p>使用 <strong>cookie-session</strong> 这个中间件</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> session = <span class="built_in">require</span>(<span class="string">&#x27;cookie-session&#x27;</span>)</span><br><span class="line"></span><br><span class="line">app.use(session(&#123;</span><br><span class="line">	<span class="attr">name</span>:<span class="string">&#x27;sessionId&#x27;</span>,	<span class="comment">// 这就是存在cookie里的cookie名</span></span><br><span class="line">	<span class="attr">secret</span>:<span class="string">&#x27;cscscscsca&#x27;</span>,	<span class="comment">// 这的secret是对sessionid进行加密，防止伪造</span></span><br><span class="line">	<span class="attr">maxAge</span>:<span class="number">20</span>*<span class="number">60</span>*<span class="number">1000</span>,</span><br><span class="line">    <span class="attr">rolling</span>:<span class="literal">true</span>	<span class="comment">// 让时间滚动刷新</span></span><br><span class="line">&#125;))</span><br><span class="line"></span><br><span class="line">app.get(<span class="string">&#x27;/&#x27;</span>,<span class="function">(<span class="params">req,res</span>)=&gt;</span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (req.session[<span class="string">&#x27;count&#x27;</span>]) &#123;</span><br><span class="line">        req.session[<span class="string">&#x27;count&#x27;</span>] += <span class="number">1</span></span><br><span class="line">        <span class="comment">// 这里就获取到session值</span></span><br><span class="line">        res.send(&#123; <span class="attr">cookie</span>: <span class="string">&#x27;cookie页面&#x27;</span>, <span class="attr">count</span>: req.session[<span class="string">&#x27;count&#x27;</span>] &#125;)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        res.send(<span class="string">&#x27;no cookie of count&#x27;</span>)</span><br><span class="line">        <span class="comment">// 这里就创建一个session值</span></span><br><span class="line">        req.session[<span class="string">&#x27;count&#x27;</span>] = <span class="number">1</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>



<h3 id="连接数据库"><a href="#连接数据库" class="headerlink" title="连接数据库"></a>连接数据库</h3><p>数据库的类型这里就先记录一个mysql吧</p>
<p>需要安装一个mysql的中间件，然后进行连接，进行crud</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">//连接mysql数据库的文件</span></span><br><span class="line"><span class="keyword">const</span> mysql = <span class="built_in">require</span>(<span class="string">&quot;mysql&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    <span class="comment">//数据库配置</span></span><br><span class="line">    <span class="attr">config</span>: &#123;</span><br><span class="line">        <span class="attr">host</span>: <span class="string">&quot;localhost&quot;</span>, <span class="comment">// 数据库的域名和端口</span></span><br><span class="line">        <span class="attr">port</span>: <span class="string">&quot;3306&quot;</span>,</span><br><span class="line">        <span class="attr">user</span>: <span class="string">&quot;root&quot;</span>,</span><br><span class="line">        <span class="attr">password</span>: <span class="string">&quot;xxxxx&quot;</span>,</span><br><span class="line">        <span class="attr">database</span>: <span class="string">&quot;exapp&quot;</span>	<span class="comment">// 数据库名</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 连接数据库，使用mysql的连接池</span></span><br><span class="line">    <span class="comment">// 连接池的对象</span></span><br><span class="line">    <span class="attr">sqlConnect</span>: <span class="function"><span class="keyword">function</span> (<span class="params">sql, sqlArr, callBack</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">let</span> pool = mysql.createPool(<span class="built_in">this</span>.config);</span><br><span class="line">        <span class="comment">// 每一次连接查询完后都需要关闭连接</span></span><br><span class="line">        pool.getConnection(<span class="function">(<span class="params">err, conn</span>) =&gt;</span> &#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">&quot;数据库连接&quot;</span>);</span><br><span class="line">            <span class="keyword">if</span> (err) &#123;</span><br><span class="line">                <span class="built_in">console</span>.log(<span class="string">&quot;连接失败&quot;</span>);</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 事件驱动回调,执行数据库操作</span></span><br><span class="line">            conn.query(sql, sqlArr, callBack);</span><br><span class="line">            <span class="comment">// 释放连接</span></span><br><span class="line">            conn.release();</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 操纵user信息的promise回调</span></span><br><span class="line">    <span class="attr">asyncSqlconnect</span>: <span class="function"><span class="keyword">function</span> (<span class="params">asyncSql, sqlArr</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">let</span> pool = mysql.createPool(<span class="built_in">this</span>.config);</span><br><span class="line">            pool.getConnection(<span class="function">(<span class="params">err, conn</span>) =&gt;</span> &#123;</span><br><span class="line">                <span class="built_in">console</span>.log(<span class="string">&quot;数据库连接&quot;</span>, asyncSql);</span><br><span class="line">                <span class="keyword">if</span> (err) &#123;</span><br><span class="line">                    reject(err);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    conn.query(asyncSql, sqlArr, <span class="function">(<span class="params">err, data</span>) =&gt;</span> &#123;</span><br><span class="line">                        <span class="keyword">if</span> (err) &#123;</span><br><span class="line">                            reject(err);</span><br><span class="line">                        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                            <span class="comment">// 将异步操作的结果作为参数传递出去</span></span><br><span class="line">                            resolve(data);</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;);</span><br><span class="line">                    <span class="comment">// 释放连接</span></span><br><span class="line">                    conn.release();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;).catch(<span class="function">(<span class="params">err</span>) =&gt;</span> &#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">&quot;catch&quot;</span>, err);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> dbconfig = <span class="built_in">require</span>(<span class="string">&#x27;../util/dbconfig&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> sql = <span class="string">&quot;select * from post where cate_id=?&quot;</span>;</span><br><span class="line">    <span class="comment">// 从res里面的query参数里传入的查询参数,这里的sqlArr是要去数据库里面进行查询的</span></span><br><span class="line"><span class="keyword">let</span> sqlArr = [id];</span><br><span class="line"><span class="keyword">let</span> callBack = <span class="function">(<span class="params">err, data</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (err) &#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">&quot;查询出错&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 发送到页面</span></span><br><span class="line">            res.send(&#123;</span><br><span class="line">                <span class="attr">list</span>: data,</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">dbconfig.sqlConnect(sql, sqlArr, callBack);</span><br></pre></td></tr></table></figure>

<h3 id="文件上传"><a href="#文件上传" class="headerlink" title="文件上传"></a>文件上传</h3><p>需要用到 <a target="_blank" rel="noopener" href="https://blog.csdn.net/kaelyn_X/article/details/78822006?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&dist_request_id=c4312e53-ba8d-4a0e-a632-cb3429f8bd82&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control"><strong>multer</strong></a> 中间件，需要安装</p>
<p><a target="_blank" rel="noopener" href="https://github.com/expressjs/multer/blob/master/doc/README-zh-cn.md">multer-github</a></p>
<p>Multer 会添加一个 <code>body</code> 对象 以及 <code>file</code> 或 <code>files</code> 对象 到 express 的 <code>request</code> 对象中。 <code>body</code> 对象包含表单的文本域信息，<code>file</code> 或 <code>files</code> 对象包含对象表单上传的文件信息</p>
<p><strong>注意</strong>: Multer 不会处理任何非 <code>multipart/form-data</code> 类型的表单数据。所以xxx-urlencode这些还是使用body-parser吧</p>
<p>前端使用 POST 方法，表单 enctype 属性设置为 multipart/form-data。</p>
<p>每个文件具有下面的信息:</p>
<table>
<thead>
<tr>
<th>Key</th>
<th>Description</th>
<th>Note</th>
</tr>
</thead>
<tbody><tr>
<td><code>fieldname</code></td>
<td>Field name 由表单指定</td>
<td></td>
</tr>
<tr>
<td><code>originalname</code></td>
<td>用户计算机上的文件的名称</td>
<td></td>
</tr>
<tr>
<td><code>encoding</code></td>
<td>文件编码</td>
<td></td>
</tr>
<tr>
<td><code>mimetype</code></td>
<td>文件的 MIME 类型</td>
<td></td>
</tr>
<tr>
<td><code>size</code></td>
<td>文件大小（字节单位）</td>
<td></td>
</tr>
<tr>
<td><code>destination</code></td>
<td>保存路径</td>
<td><code>DiskStorage</code></td>
</tr>
<tr>
<td><code>filename</code></td>
<td>保存在 <code>destination</code> 中的文件名</td>
<td><code>DiskStorage</code></td>
</tr>
<tr>
<td><code>path</code></td>
<td>已上传文件的完整路径</td>
<td><code>DiskStorage</code></td>
</tr>
<tr>
<td><code>buffer</code></td>
<td>一个存放了整个文件的 <code>Buffer</code></td>
<td><code>MemoryStorage</code></td>
</tr>
</tbody></table>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// upload.js</span></span><br><span class="line"><span class="keyword">const</span> express = <span class="built_in">require</span>(<span class="string">&#x27;express&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> router = express.Router();</span><br><span class="line"><span class="keyword">const</span> multer = <span class="built_in">require</span>(<span class="string">&#x27;multer&#x27;</span>);</span><br><span class="line"> </span><br><span class="line"><span class="keyword">let</span> upload = multer(&#123;</span><br><span class="line">    <span class="comment">// dest: &#x27;uploads/&#x27; 最简单的方式，只指定一下存储的位置</span></span><br><span class="line">    <span class="comment">// 这里的storage是控制文件存储在哪里，这里是存在磁盘上，还有存在内存里的，但不使用</span></span><br><span class="line">    <span class="attr">storage</span>: multer.diskStorage(&#123;</span><br><span class="line">        <span class="attr">destination</span>: <span class="function"><span class="keyword">function</span> (<span class="params">req, file, cb</span>) </span>&#123;</span><br><span class="line">            <span class="comment">// 这里是地址</span></span><br><span class="line">            cb(<span class="literal">null</span>, <span class="string">&#x27;./uploads/&#x27;</span>);</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="comment">// 用于确定存储在上面目录里的文件的文件名</span></span><br><span class="line">        <span class="attr">filename</span>: <span class="function"><span class="keyword">function</span> (<span class="params">req, file, cb</span>) </span>&#123;</span><br><span class="line">            <span class="comment">// 这里对图片名加上时间戳</span></span><br><span class="line">            <span class="keyword">var</span> changedName = (<span class="keyword">new</span> <span class="built_in">Date</span>().getTime())+<span class="string">&#x27;-&#x27;</span>+file.originalname;</span><br><span class="line">            cb(<span class="literal">null</span>, changedName);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;);</span><br><span class="line"> </span><br><span class="line"><span class="comment">//单个文件上传，注意在这里使用了中间件</span></span><br><span class="line">router.post(<span class="string">&#x27;/single&#x27;</span>, upload.single(<span class="string">&#x27;singleFileOfName&#x27;</span>), <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// req.file 是 `singleFileOfName` 文件的信息</span></span><br><span class="line">    <span class="built_in">console</span>.log(req.file);</span><br><span class="line">    res.json(&#123;</span><br><span class="line">        <span class="attr">code</span>: <span class="string">&#x27;0000&#x27;</span>,</span><br><span class="line">        <span class="attr">type</span>: <span class="string">&#x27;single&#x27;</span>,</span><br><span class="line">        <span class="attr">originalname</span>: req.file.originalname,</span><br><span class="line">        <span class="attr">path</span>: req.file.path</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;);</span><br><span class="line"> </span><br><span class="line"><span class="comment">//多个文件上传，注意在这里使用了中间件</span></span><br><span class="line">router.post(<span class="string">&#x27;/multer&#x27;</span>, upload.array(<span class="string">&#x27;multerFile&#x27;</span>), <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// req.files 是 `multerFile` 文件数组的信息</span></span><br><span class="line">    <span class="built_in">console</span>.log(req.files);</span><br><span class="line">    <span class="keyword">let</span> fileList = [];</span><br><span class="line">    req.files.map(<span class="function">(<span class="params">elem</span>) =&gt;</span> &#123;</span><br><span class="line">        fileList.push(&#123;</span><br><span class="line">            <span class="attr">originalname</span>: elem.originalname</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;);</span><br><span class="line">    res.json(&#123;</span><br><span class="line">        <span class="attr">code</span>: <span class="string">&#x27;0000&#x27;</span>,</span><br><span class="line">        <span class="attr">type</span>: <span class="string">&#x27;multer&#x27;</span>,</span><br><span class="line">        <span class="attr">fileList</span>: fileList</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"> </span><br><span class="line"><span class="built_in">module</span>.exports = router;</span><br></pre></td></tr></table></figure>

<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">//app.js引入路由</span></span><br><span class="line"><span class="keyword">const</span> multerUpload = <span class="built_in">require</span>(<span class="string">&#x27;./routes/upload&#x27;</span>);</span><br><span class="line"><span class="comment">//使用路由</span></span><br><span class="line">app.use(<span class="string">&#x27;/upload&#x27;</span>, multerUpload);</span><br></pre></td></tr></table></figure>



<p>如果你需要处理一个只有文本域的表单，你应当使用 <code>.none()</code>:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">var express = require(&#x27;express&#x27;)</span><br><span class="line">var app = express()</span><br><span class="line">var multer  = require(&#x27;multer&#x27;)</span><br><span class="line">var upload = multer()</span><br><span class="line"></span><br><span class="line">app.post(&#x27;/profile&#x27;, upload.none(), function (req, res, next) &#123;</span><br><span class="line">  // req.body 包含文本域</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p>还有一些其他的东西就不记录了，需要的时候看文档就行</p>
<h3 id="接口设计"><a href="#接口设计" class="headerlink" title="接口设计"></a>接口设计</h3><p>前后端分离式开发需要进行数据交互，传输的数据被偷窥、被抓包、被伪造时有发生，需要设计一套比较安全的API接口方案</p>
<p>并不是所有的接口都需要考虑安全的，有些接口是公开的，任何人只要知道地址都可以调用，对于一些项目中需要用户登录才能访问的接口才需要考虑安全问题。</p>
<p>一般解决的方案有以下几类:</p>
<ul>
<li>token令牌认证[jwt   json web token]</li>
<li>AK (app key)&amp;SK (secret key)【用户名&amp;密码】</li>
<li>时间戳超时验证+签名算法字符串</li>
<li>URL签名(算法，非对称算法)·数据脱敏(防范数据库数据泄露)</li>
<li> HTTPS</li>
<li>数字证书(防运营商)</li>
<li>iP黑/白名单(服务器层面的限制，apache、nginx)</li>
<li>oAuth2.0</li>
</ul>
<p>以jwt为例</p>
<h4 id="jwt"><a href="#jwt" class="headerlink" title="jwt"></a>jwt</h4><p>使用JWT进行鉴权的工作流程如下(重点):</p>
<ul>
<li>用户使用用户名密码来请求服务器</li>
<li>服务器进行验证用户的信息(查数据库)</li>
<li>服务器通过验证发送给用户一个token(令牌)</li>
<li>客户端存储token (Vuex+localStorage)，并在每次请求时附送上这个token值</li>
<li>服务端验证token值，并返回数据</li>
</ul>
<p>jwt由 头部、载荷、签名三部分构成</p>
<p>在nodejs里面使用jwt的话，需要安装一个中间件</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">npm install jsonwebtoken -S</span><br></pre></td></tr></table></figure>



<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> jwt = <span class="built_in">require</span>(<span class="string">&#x27;jsonwebtoken&#x27;</span>);</span><br><span class="line"><span class="keyword">let</span> secret = <span class="string">&#x27;token&#x27;</span></span><br><span class="line"><span class="keyword">const</span> Token = &#123;</span><br><span class="line">  <span class="attr">encrypt</span>:<span class="function"><span class="keyword">function</span>(<span class="params">data,time</span>)</span>&#123; <span class="comment">//data需加密数据，time到期时间</span></span><br><span class="line">    <span class="comment">// secret加密密钥，expiresIn到期时间，issuer发行人</span></span><br><span class="line">    <span class="keyword">return</span> jwt.sign(data, secret, &#123;<span class="attr">expiresIn</span>:time,<span class="attr">issuer</span>:<span class="string">&#x27;cs&#x27;</span>&#125;)</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">decrypt</span>:<span class="function"><span class="keyword">function</span>(<span class="params">token</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> data = jwt.verify(token, secret);</span><br><span class="line">      <span class="keyword">return</span> &#123;</span><br><span class="line">        <span class="attr">token</span>:<span class="literal">true</span>,</span><br><span class="line">        <span class="attr">id</span>:data.id</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">      <span class="keyword">return</span> &#123;</span><br><span class="line">        <span class="attr">token</span>:<span class="literal">false</span>,</span><br><span class="line">        <span class="attr">data</span>:e</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">module</span>.exports = Token;</span><br></pre></td></tr></table></figure>



<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> Token = <span class="built_in">require</span>(<span class="string">&#x27;../utils/token&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//code……</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> token = Token.encrypt(&#123;<span class="attr">id</span>:user.id&#125;,<span class="string">&#x27;15d&#x27;</span>);  <span class="comment">//将user.id加密，设置有效期15天，返回token</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//code……</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//解密</span></span><br><span class="line"><span class="keyword">let</span> data = Token.decrypt(ctx.header.authorization);  <span class="comment">//将请求头的token取出解密</span></span><br><span class="line"><span class="keyword">if</span> (data.token) &#123;</span><br><span class="line">    <span class="comment">//有效token</span></span><br><span class="line">&#125;<span class="keyword">else</span>&#123;</span><br><span class="line">    <span class="comment">//无效token</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意前端网络请求的请求头需要包含一个属性 <strong>authorization</strong> 来存储token</p>
<hr>
<h2 id="koa2"><a href="#koa2" class="headerlink" title="koa2"></a>koa2</h2><p>主要是对B站的一个koa2的教程的简单记录</p>
<p><a target="_blank" rel="noopener" href="https://www.koajs.com.cn/">koa2官网</a></p>
<p>主要是前面的express都是使用回调函数来进行开发，写多了是在是有一些难受，同时还有回调嵌套，同步调用callback返回的数据，带来不一致性等，所以出现了koa框架，此外还有一个阿里的基于koa的企业级框架<a target="_blank" rel="noopener" href="https://eggjs.org/zh-cn/intro/egg-and-koa.html">egg</a>，但目前不去管</p>
<h3 id="简单介绍-2"><a href="#简单介绍-2" class="headerlink" title="简单介绍"></a>简单介绍</h3><p>因为koa里面使用了async/await 所以node的版本需要高于8</p>
<h4 id="初始化项目-1"><a href="#初始化项目-1" class="headerlink" title="初始化项目"></a>初始化项目</h4><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 需要先自己创建一个目录，然后在这个目录里面进行创建koa项目</span></span><br><span class="line">npm init -y <span class="comment"># 创建pakcage.json</span></span><br><span class="line">npm install koa --save</span><br></pre></td></tr></table></figure>

<p>然后在里面创建 <strong>app.js</strong> 文件，其实可以在 <strong>pakcage.json</strong> 里面的main属性里看见</p>
<p>之后我们的代码就是在app.js里面写的了</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 代码示例</span></span><br><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">&#x27;koa&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa()</span><br><span class="line"><span class="comment">// 在ctx里面包含了express的req和res</span></span><br><span class="line">app.use(<span class="keyword">async</span> (ctx) =&gt; &#123;</span><br><span class="line">    <span class="comment">// 使用body属性返回数据</span></span><br><span class="line">    ctx.body = <span class="string">&#x27;hello koa2&#x27;</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">app.listen(<span class="number">3000</span>)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">&#x27;app start&#x27;</span>)</span><br></pre></td></tr></table></figure>

<h3 id="路由-1"><a href="#路由-1" class="headerlink" title="路由"></a>路由</h3><p>这里需要安装 <strong><a target="_blank" rel="noopener" href="https://github.com/koajs/router/blob/HEAD/API.md">koa-router</a></strong> ，然后在文件里面引入并使用</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">npm i @koa/router</span><br></pre></td></tr></table></figure>

<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">&#x27;koa&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> Router = <span class="built_in">require</span>(<span class="string">&#x27;@koa/router&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa();</span><br><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> Router();</span><br><span class="line"></span><br><span class="line">router.get(<span class="string">&#x27;/&#x27;</span>, <span class="function">(<span class="params">ctx, next</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// ctx.router available</span></span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// 还能这样写</span></span><br><span class="line">router</span><br><span class="line">    .get(<span class="string">&#x27;/a&#x27;</span>, <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">        <span class="comment">// ctx.body = &#x27;Hello World!&#x27;</span></span><br><span class="line">        ctx.body = &#123; <span class="attr">a</span>: <span class="string">&#x27;ca&#x27;</span> &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">    .post(<span class="string">&#x27;/users&#x27;</span>, <span class="function">(<span class="params">ctx, next</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">    &#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 加载路由中间件</span></span><br><span class="line">app.use(router.routes()) <span class="comment">// 启动路由</span></span><br><span class="line">  .use(router.allowedMethods()); <span class="comment">// 根据ctx.status自动设置响应头</span></span><br></pre></td></tr></table></figure>

<p>还有一个 <strong>router.prefix(‘api’)</strong> 会为当前路由文件的所有路由都添加一个api的前缀</p>
<h4 id="获取参数"><a href="#获取参数" class="headerlink" title="获取参数"></a>获取参数</h4><p>可以直接在ctx里面获取，也可使用ctx里面的request获取</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 获取get值，这里的是对象的形式</span></span><br><span class="line"><span class="keyword">let</span> query = ctx.query</span><br><span class="line"><span class="comment">// 还有字符串形式的query	 ctx.querystring</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取url</span></span><br><span class="line"><span class="keyword">let</span> url = ctx.url</span><br><span class="line">……</span><br></pre></td></tr></table></figure>



<h4 id="动态路由"><a href="#动态路由" class="headerlink" title="动态路由"></a>动态路由</h4><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">router.get(<span class="string">&#x27;/users/:id&#x27;</span>, <span class="function">(<span class="params">ctx, next</span>) =&gt;</span> &#123;	</span><br><span class="line"> <span class="comment">// 获取路由的参数</span></span><br><span class="line">     <span class="built_in">console</span>.log(ctx.params)	<span class="comment">//  /users/a		&#123; id: &#x27;a&#x27; &#125;</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>



<h4 id="模块化"><a href="#模块化" class="headerlink" title="模块化"></a>模块化</h4><p>创建一个user.js文件，然后在里面编写路由，之后引入到app.js里面</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// user.js</span></span><br><span class="line"><span class="keyword">const</span> Router = <span class="built_in">require</span> (<span class="string">&#x27;koa-router&#x27;</span>)</span><br><span class="line"><span class="keyword">let</span> router = <span class="keyword">new</span> Router()</span><br><span class="line">router.prefix(<span class="string">&#x27;api&#x27;</span>)</span><br><span class="line">router.get(<span class="string">&#x27;/&#x27;</span>,<span class="keyword">async</span> (ctx)=&gt;&#123;</span><br><span class="line">    ctx.body=<span class="string">&quot;这是用户操作首页&quot;</span></span><br><span class="line">&#125;)</span><br><span class="line">router.get(<span class="string">&#x27;/register&#x27;</span>,<span class="keyword">async</span> (ctx)=&gt;&#123;</span><br><span class="line">    ctx.body=<span class="string">&quot;用户注册接口&quot;</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="built_in">module</span>.exports=router;</span><br></pre></td></tr></table></figure>

<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app.js</span></span><br><span class="line"><span class="keyword">const</span> Router = <span class="built_in">require</span>(<span class="string">&#x27;koa-router&#x27;</span>)</span><br><span class="line"><span class="keyword">let</span> router = <span class="keyword">new</span> Router();</span><br><span class="line"><span class="comment">// 引入模块</span></span><br><span class="line"><span class="keyword">let</span> user = <span class="built_in">require</span>(<span class="string">&#x27;./appApi/user.js&#x27;</span>)</span><br><span class="line"><span class="comment">// 装载所有子路由</span></span><br><span class="line">router.use(<span class="string">&#x27;/user&#x27;</span>,user.routes())</span><br><span class="line"><span class="comment">// 加载路由中间件</span></span><br><span class="line">app.use(router.routes())</span><br><span class="line">app.use(router.allowedMethods())</span><br></pre></td></tr></table></figure>



<h3 id="中间件-1"><a href="#中间件-1" class="headerlink" title="中间件"></a>中间件</h3><p>其定义跟上面express的中间件差不多，都是在匹配路由之前或完成之后做的一系列操作</p>
<p>感觉用法也跟express差不多</p>
<p>但有一个注意点，koa的中间件执行顺序是洋葱型的，即路由的函数执行完之后，在 next() 后面的代码会继续执行<img src="https://camo.githubusercontent.com/d80cf3b511ef4898bcde9a464de491fa15a50d06/68747470733a2f2f7261772e6769746875622e636f6d2f66656e676d6b322f6b6f612d67756964652f6d61737465722f6f6e696f6e2e706e67" alt="img" style="zoom:80%;" /></p>
<h4 id="路由中间件"><a href="#路由中间件" class="headerlink" title="路由中间件"></a>路由中间件</h4><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 感觉这个有一些特别</span></span><br><span class="line">router.get(<span class="string">&#x27;/&#x27;</span>, <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;cs&#x27;</span>)</span><br><span class="line">    <span class="keyword">await</span> next()</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 配置路由</span></span><br><span class="line">router.get(<span class="string">&#x27;/&#x27;</span>, <span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">        <span class="comment">// ctx.body = &#x27;Hello World!&#x27;</span></span><br><span class="line">        <span class="comment">// ctx.body = &#123; a: &#x27;ca&#x27; &#125;</span></span><br><span class="line">        ctx.body = ctx</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p>只会是这个路由匹配到之后，这个中间件才会运行</p>
<h4 id="错误处理中间件"><a href="#错误处理中间件" class="headerlink" title="错误处理中间件"></a>错误处理中间件</h4><p>注意在koa里面，中间件会先于路由执行</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.use(<span class="keyword">async</span> (ctx, next) =&gt; &#123;</span><br><span class="line">    <span class="comment">// 当前路由完成之后，继续向下匹配</span></span><br><span class="line">    <span class="keyword">await</span> next()</span><br><span class="line">    <span class="keyword">if</span> (ctx.status == <span class="number">404</span>) &#123;</span><br><span class="line">        ctx.status = <span class="number">404</span></span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;404&#x27;</span>)</span><br><span class="line">        ctx.body = <span class="string">&#x27;404页面&#x27;</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;路由被匹配上了&#x27;</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>



<h3 id="post提交数据"><a href="#post提交数据" class="headerlink" title="post提交数据"></a>post提交数据</h3><p>需要使用 <strong>koa-bodyparser</strong>  这个中间件，需要安装，然后使用，注意这个不能得到formdata的数据，其实可以直接在koa2里面使用koa-body，能直接处理多种数据上传（包括文件上传，表单上传等）</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> Koa = <span class="built_in">require</span>(<span class="string">&#x27;koa&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> bodyParser = <span class="built_in">require</span>(<span class="string">&#x27;koa-bodyparser&#x27;</span>);</span><br><span class="line"> </span><br><span class="line"><span class="keyword">var</span> app = <span class="keyword">new</span> Koa();</span><br><span class="line">app.use(bodyParser());</span><br><span class="line"> </span><br><span class="line">app.use(<span class="keyword">async</span> ctx =&gt; &#123;</span><br><span class="line">  <span class="comment">// 通过这个来获取post的表单里面的数据，注意其格式是</span></span><br><span class="line">  ctx.body = ctx.request.body;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>



<h3 id="静态资源"><a href="#静态资源" class="headerlink" title="静态资源"></a>静态资源</h3><p><strong>koa-static</strong> 中间件</p>
<p>先安装，然后使用</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">&#x27;koa&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa();</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> serve = <span class="built_in">require</span>(<span class="string">&#x27;koa-static&#x27;</span>)</span><br><span class="line"><span class="comment">// 这里就指定静态资源目录是static目录</span></span><br><span class="line">app.use(serve(<span class="string">&#x27;static&#x27;</span>))</span><br><span class="line"><span class="comment">// app.use(__dirname+&#x27;/static&#x27;) 绝对地址的方式</span></span><br><span class="line"><span class="comment">// app.use(serve(&#x27;public&#x27;)) 还能配置多个静态资源目录</span></span><br></pre></td></tr></table></figure>

<p>使用示例</p>
<p>如</p>
<p><img src="https://gitee.com/cheng_song/picgo-img/raw/master/img/image-20210304162710544.png" alt="image-20210304162710544"></p>
<p>然后进行访问 <strong><a target="_blank" rel="noopener" href="http://localhost:3000/my.txt">http://localhost:3000/my.txt</a></strong> 就能得到这个静态资源</p>
<p>当得到一个网址的时候，会首先去static目录里面找有没有这个文件，然后找到就返回这个文件，没有就继续 next 向下匹配</p>
<h3 id="上传文件"><a href="#上传文件" class="headerlink" title="上传文件"></a>上传文件</h3><p>会使用到一个中间件 <strong><a target="_blank" rel="noopener" href="http://www.ptbird.cn/koa-body.html">koa-body</a></strong> ，需要安装，然后配置一些东西，好像也可以直接使用这个替代上面的 koa-bodyparser 来获取表单数据，直接一个中间件处理两种数据</p>
<h4 id="koa-body-的基本参数"><a href="#koa-body-的基本参数" class="headerlink" title="koa-body 的基本参数"></a><code>koa-body</code> 的基本参数</h4><table>
<thead>
<tr>
<th align="left">参数名</th>
<th align="left">描述</th>
<th align="left">类型</th>
<th align="left">默认值</th>
</tr>
</thead>
<tbody><tr>
<td align="left">patchNode</td>
<td align="left">将请求体打到原生 node.js 的<code>ctx.req</code>中</td>
<td align="left">Boolean</td>
<td align="left"><code>false</code></td>
</tr>
<tr>
<td align="left">patchKoa</td>
<td align="left">将请求体打到 koa 的 <code>ctx.request</code> 中</td>
<td align="left">Boolean</td>
<td align="left"><code>true</code></td>
</tr>
<tr>
<td align="left">jsonLimit</td>
<td align="left">JSON 数据体的大小限制</td>
<td align="left">String / Integer</td>
<td align="left"><code>1mb</code></td>
</tr>
<tr>
<td align="left">formLimit</td>
<td align="left">限制表单请求体的大小</td>
<td align="left">String / Integer</td>
<td align="left"><code>56kb</code></td>
</tr>
<tr>
<td align="left">textLimit</td>
<td align="left">限制 text body 的大小</td>
<td align="left">String / Integer</td>
<td align="left"><code>56kb</code></td>
</tr>
<tr>
<td align="left">encoding</td>
<td align="left">表单的默认编码</td>
<td align="left">String</td>
<td align="left"><code>utf-8</code></td>
</tr>
<tr>
<td align="left">multipart</td>
<td align="left">是否支持 <code>multipart-formdate</code> 的表单</td>
<td align="left">Boolean</td>
<td align="left"><code>false</code></td>
</tr>
<tr>
<td align="left">urlencoded</td>
<td align="left">是否支持 <code>urlencoded</code> 的表单</td>
<td align="left">Boolean</td>
<td align="left"><code>true</code></td>
</tr>
<tr>
<td align="left">text</td>
<td align="left">是否解析 <code>text/plain</code> 的表单</td>
<td align="left">Boolean</td>
<td align="left"><code>true</code></td>
</tr>
<tr>
<td align="left">json</td>
<td align="left">是否解析 <code>json</code> 请求体</td>
<td align="left">Boolean</td>
<td align="left"><code>true</code></td>
</tr>
<tr>
<td align="left">jsonStrict</td>
<td align="left">是否使用 json 严格模式，<code>true</code> 会只处理数组和对象</td>
<td align="left">Boolean</td>
<td align="left"><code>true</code></td>
</tr>
<tr>
<td align="left">formidable</td>
<td align="left">配置更多的关于 <code>multipart</code> 的选项</td>
<td align="left">Object</td>
<td align="left"><code>&#123;&#125;</code></td>
</tr>
<tr>
<td align="left">onError</td>
<td align="left">错误处理</td>
<td align="left">Function</td>
<td align="left"><code>function()&#123;&#125;</code></td>
</tr>
<tr>
<td align="left">stict</td>
<td align="left">严格模式,启用后不会解析 <code>GET, HEAD, DELETE </code>请求</td>
<td align="left">Boolean</td>
<td align="left"><code>true</code></td>
</tr>
</tbody></table>
<h4 id="formidable-的相关配置参数"><a href="#formidable-的相关配置参数" class="headerlink" title="formidable 的相关配置参数"></a><code>formidable</code> 的相关配置参数</h4><table>
<thead>
<tr>
<th align="left">参数名</th>
<th align="left">描述</th>
<th align="left">类型</th>
<th align="left">默认值</th>
</tr>
</thead>
<tbody><tr>
<td align="left">maxFields</td>
<td align="left">限制字段的数量</td>
<td align="left">Integer</td>
<td align="left"><code>1000</code></td>
</tr>
<tr>
<td align="left">maxFieldsSize</td>
<td align="left">限制字段的最大大小</td>
<td align="left">Integer</td>
<td align="left"><code>2 * 1024 * 1024</code></td>
</tr>
<tr>
<td align="left">uploadDir</td>
<td align="left">文件上传的文件夹</td>
<td align="left">String</td>
<td align="left"><code>os.tmpDir()</code></td>
</tr>
<tr>
<td align="left">keepExtensions</td>
<td align="left">保留原来的文件后缀</td>
<td align="left">Boolean</td>
<td align="left"><code>false</code></td>
</tr>
<tr>
<td align="left">hash</td>
<td align="left">如果要计算文件的 hash，则可以选择 <code>md5/sha1</code></td>
<td align="left">String</td>
<td align="left"><code>false</code></td>
</tr>
<tr>
<td align="left">multipart</td>
<td align="left">是否支持多文件上传</td>
<td align="left">Boolean</td>
<td align="left"><code>true</code></td>
</tr>
<tr>
<td align="left">onFileBegin</td>
<td align="left">文件上传前的一些设置操作</td>
<td align="left">Function</td>
<td align="left"><code>function(name,file)&#123;&#125;</code></td>
</tr>
</tbody></table>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app.js</span></span><br><span class="line"><span class="keyword">const</span> koaBody = <span class="built_in">require</span>(<span class="string">&#x27;koa-body&#x27;</span>)</span><br><span class="line">app.use(</span><br><span class="line">    koaBody(&#123;</span><br><span class="line">        <span class="attr">multipart</span>: <span class="literal">true</span>, <span class="comment">//支持文件上传</span></span><br><span class="line">        <span class="attr">formidable</span>: &#123;	<span class="comment">// 这里还有一些其他的配置，需要时去官网文档看</span></span><br><span class="line">            <span class="attr">maxFileSize</span>: <span class="number">2000</span> * <span class="number">1024</span> * <span class="number">1024</span>, <span class="comment">// 设置上传文件大小最大限制，默认2M</span></span><br><span class="line">        &#125;,</span><br><span class="line">    &#125;),</span><br><span class="line">)</span><br></pre></td></tr></table></figure>

<p>然后创建一个js文件专门处理文件上传</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// upload.js</span></span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">&#x27;path&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">&#x27;fs&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> uploadimg = <span class="function">(<span class="params">ctx</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">JSON</span>.stringify(ctx.request, <span class="literal">null</span>, <span class="string">&#x27; &#x27;</span>))</span><br><span class="line">    <span class="keyword">let</span> remotefilePath = <span class="literal">null</span></span><br><span class="line">    <span class="keyword">if</span> (ctx.request.files[<span class="string">&#x27;file&#x27;</span>]) &#123;</span><br><span class="line">        <span class="comment">// 创建可读流，同时在ctx.request.files里面选出属性名为file的文件的path创建流</span></span><br><span class="line">        <span class="keyword">const</span> reader = fs.createReadStream(ctx.request.files[<span class="string">&#x27;file&#x27;</span>][<span class="string">&#x27;path&#x27;</span>])</span><br><span class="line">        <span class="comment">// 创建一个文件保存在哪里的路径</span></span><br><span class="line">        <span class="keyword">let</span> time = <span class="keyword">new</span> <span class="built_in">Date</span>().getTime()</span><br><span class="line">        <span class="keyword">let</span> filePath = <span class="string">`<span class="subst">$&#123;path.resolve(__dirname, <span class="string">&#x27;../upload&#x27;</span>)&#125;</span>/<span class="subst">$&#123;time&#125;</span><span class="subst">$&#123;ctx.request.files[<span class="string">&#x27;file&#x27;</span>][<span class="string">&#x27;name&#x27;</span>]&#125;</span>`</span></span><br><span class="line">        <span class="comment">// 这就是返回给前端进行图片访问的url，需要配合前面设置的静态资源目录路径</span></span><br><span class="line">        remotefilePath = <span class="string">`http://127.0.0.1/upload/<span class="subst">$&#123;time&#125;</span><span class="subst">$&#123;ctx.request.files[<span class="string">&#x27;file&#x27;</span>][<span class="string">&#x27;name&#x27;</span>]&#125;</span>`</span></span><br><span class="line">        <span class="comment">// 创建可写流，即保存文件</span></span><br><span class="line">        <span class="keyword">const</span> upStream = fs.createWriteStream(filePath)</span><br><span class="line">        <span class="comment">// 可读流通过管道写入可写流</span></span><br><span class="line">        reader.pipe(upStream)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> remotefilePath</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = uploadimg</span><br></pre></td></tr></table></figure>

<p>之后就可以用了</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// api.js</span></span><br><span class="line"><span class="keyword">const</span> router = <span class="built_in">require</span>(<span class="string">&#x27;koa-router&#x27;</span>)()</span><br><span class="line"><span class="keyword">const</span> upload = <span class="built_in">require</span>(<span class="string">&#x27;./upload&#x27;</span>)</span><br><span class="line"></span><br><span class="line">router.post(<span class="string">&#x27;/uploadImg&#x27;</span>, <span class="keyword">async</span> <span class="function"><span class="keyword">function</span> (<span class="params">ctx, next</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> imgUrl = <span class="keyword">await</span> upload(ctx)</span><br><span class="line">    <span class="keyword">if</span> (imgUrl) &#123;</span><br><span class="line">        ctx.body = &#123;</span><br><span class="line">            <span class="attr">data</span>: imgUrl,</span><br><span class="line">            <span class="attr">message</span>: <span class="string">&#x27;文件上传成功&#x27;</span>,</span><br><span class="line">            <span class="attr">code</span>: <span class="string">&#x27;0&#x27;</span>,</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        ctx.body = &#123;</span><br><span class="line">            <span class="attr">data</span>: imgUrl,</span><br><span class="line">            <span class="attr">message</span>: <span class="string">&#x27;文件上传失败&#x27;</span>,</span><br><span class="line">            <span class="attr">code</span>: <span class="string">&#x27;1&#x27;</span>,</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = route</span><br></pre></td></tr></table></figure>



<h4 id="另一种配置"><a href="#另一种配置" class="headerlink" title="另一种配置"></a><a target="_blank" rel="noopener" href="http://www.ptbird.cn/koa-body-diy-upload-dir-and-filename.html">另一种配置</a></h4><p>当然，koa-body给我们提供了相应的接口，让我么可以直接在配置的时候，就能对文件进行存储和重命名，主要是使用 <strong>onFileBegin</strong> 这个属性，具体的配置就看其他大佬写的吧</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> koaBody = <span class="built_in">require</span>(<span class="string">&#x27;koa-body&#x27;</span>)</span><br><span class="line">app.use(</span><br><span class="line">    koaBody(&#123;</span><br><span class="line">        <span class="attr">multipart</span>: <span class="literal">true</span>, <span class="comment">//支持表单上传</span></span><br><span class="line">        <span class="attr">formidable</span>: &#123;</span><br><span class="line">            <span class="attr">maxFileSize</span>: <span class="number">2000</span> * <span class="number">1024</span> * <span class="number">1024</span>, <span class="comment">// 设置上传文件大小最大限制，默认2M</span></span><br><span class="line">            <span class="attr">uploadDir</span>: path.join(__dirname, <span class="string">&#x27;upload&#x27;</span>),</span><br><span class="line">            <span class="attr">onFileBegin</span>: <span class="function">(<span class="params">name, file</span>) =&gt;</span> &#123;</span><br><span class="line">              <span class="built_in">console</span>.log(file)</span><br><span class="line">              <span class="comment">// 获取文件后缀</span></span><br><span class="line">              <span class="keyword">const</span> ext = getUploadFileExt(file.name);</span><br><span class="line">              <span class="comment">// 最终要保存到的文件夹目录</span></span><br><span class="line">              <span class="keyword">const</span> dirName = getUploadDirName();</span><br><span class="line">              <span class="keyword">const</span> dir = path.join(__dirname, <span class="string">`public/upload/<span class="subst">$&#123;dirName&#125;</span>`</span>);</span><br><span class="line">              <span class="comment">// 检查文件夹是否存在如果不存在则新建文件夹</span></span><br><span class="line">              checkDirExist(dir);</span><br><span class="line">              <span class="comment">// 获取文件名称</span></span><br><span class="line">              <span class="keyword">const</span> fileName = getUploadFileName(ext);</span><br><span class="line">              <span class="comment">// 重新覆盖 file.path 属性</span></span><br><span class="line">              file.path = <span class="string">`<span class="subst">$&#123;dir&#125;</span>/<span class="subst">$&#123;fileName&#125;</span>`</span>;</span><br><span class="line">              app.context.uploadpath = app.context.uploadpath ? app.context.uploadpath : &#123;&#125;;</span><br><span class="line">              app.context.uploadpath[name] = <span class="string">`<span class="subst">$&#123;dirName&#125;</span>/<span class="subst">$&#123;fileName&#125;</span>`</span>;</span><br><span class="line">            &#125;,</span><br><span class="line">        &#125;,</span><br><span class="line">    &#125;),</span><br><span class="line">)</span><br></pre></td></tr></table></figure>

<p>虽然感觉这样没有太大的区别，自由度没有上面的那种那么高</p>
<h3 id="跨域"><a href="#跨域" class="headerlink" title="跨域"></a>跨域</h3><p>安装 <strong>koa2-cors</strong> 中间件<br>在koa2里解决跨域的中间件叫koa2-cors，我们先进入service文件夹，然后直接使用npm来进行安装。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">npm install --save koa2-cors</span><br></pre></td></tr></table></figure>

<p>安装完成后，记得要在service/index.js文件中引入和注册（使用）一下中间件：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> cors = <span class="built_in">require</span>(<span class="string">&#x27;koa2-cors&#x27;</span>)</span><br><span class="line">app.use(cors())</span><br></pre></td></tr></table></figure>

<p>之后就能跨域了</p>
<h3 id="cookie-1"><a href="#cookie-1" class="headerlink" title="cookie"></a>cookie</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.cookies.get(name, [options])</span><br></pre></td></tr></table></figure>

<p>获得 cookie 中名为 <code>name</code> 的值，<code>options</code> 为可选参数</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.cookies.set(name, value, [options])</span><br><span class="line">ctx.cookies.set(<span class="string">&#x27;cookie&#x27;</span>, <span class="string">&#x27;setCookie&#x27;</span>, &#123;<span class="attr">maxAge</span>:<span class="number">10</span>*<span class="number">60</span>*<span class="number">1000</span>,&#125;)</span><br></pre></td></tr></table></figure>

<p>设置 cookie 中名为 <code>name</code> 的值，<code>options</code> 为可选参数：</p>
<ul>
<li><code>maxAge</code> 一个数字，表示 Date.now()到期的毫秒数</li>
<li><code>signed</code> 是否要做签名</li>
<li><code>expires</code> cookie有效期</li>
<li><code>path</code>cookie 的路径，默认为 <code>/&#39;</code> ，就是可访问这个cookie的路径</li>
<li><code>domain</code> cookie 的域，默认当前域下面所有的页面都能访问</li>
<li><code>secure</code> false 表示 cookie 通过 HTTP 协议发送，true 表示 cookie 通过 HTTPS 发送。</li>
<li><code>httpOnly</code> true 表示 cookie 只能通过 HTTP 协议发送</li>
<li><code>overwrite</code> 一个布尔值，表示是否覆盖以前设置的同名的Cookie（默认为false）。 如果为true，在设置此cookie时，将在同一请求中使用相同名称（不管路径或域）设置的所有Cookie将从Set-Cookie头部中过滤掉。</li>
</ul>
<p>注意：Koa 使用了 Express 的 <a target="_blank" rel="noopener" href="https://github.com/jed/cookies">cookies</a> 模块，options 参数只是简单地直接进行传递。</p>
<p>而且在 cookie 里面有一个bug或特性，不能直接设置中文，会出现错误。</p>
<p>只能将中文转为base64的格式进行存储，在使用的时候再转回来</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.cookies.set(<span class="string">&#x27;cs&#x27;</span>, <span class="keyword">new</span> Buffer(<span class="string">&#x27;晟松&#x27;</span>).toString(<span class="string">&#x27;base64&#x27;</span>))</span><br><span class="line">ctx.body = <span class="keyword">new</span> Buffer(ctx.cookies.get(<span class="string">&#x27;cs&#x27;</span>), <span class="string">&#x27;base64&#x27;</span>).toString()</span><br></pre></td></tr></table></figure>

<p>但好像这里面的Buffer已经被弃用了，所以能不用就不用</p>
<h3 id="session-1"><a href="#session-1" class="headerlink" title="session"></a><a target="_blank" rel="noopener" href="https://github.com/koajs/session#readme">session</a></h3><p>需要先安装 <strong>koa-session</strong> 的中间件</p>
<p>然后进行配置使用，直接使用  <strong>ctx.seesion</strong> 进行访问和设置</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> session = <span class="built_in">require</span>(<span class="string">&#x27;koa-session&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">&#x27;koa&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa();</span><br><span class="line"></span><br><span class="line">app.keys = [<span class="string">&#x27;some secret hurr&#x27;</span>]; <span class="comment">// cookie的签名，就是对cookie的一个加密值，可自行设定</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> CONFIG = &#123;</span><br><span class="line">  <span class="attr">key</span>: <span class="string">&#x27;koa.sess&#x27;</span>, <span class="comment">// cookie名</span></span><br><span class="line">  <span class="attr">maxAge</span>: <span class="number">86400000</span>,</span><br><span class="line">  <span class="attr">autoCommit</span>: <span class="literal">true</span>, </span><br><span class="line">  <span class="attr">overwrite</span>: <span class="literal">true</span>,</span><br><span class="line">  <span class="attr">httpOnly</span>: <span class="literal">true</span>, </span><br><span class="line">  <span class="attr">signed</span>: <span class="literal">true</span>, </span><br><span class="line">  <span class="attr">rolling</span>: <span class="literal">false</span>,  <span class="comment">// 每次访问都重新设置session</span></span><br><span class="line">  <span class="attr">renew</span>: <span class="literal">false</span>, </span><br><span class="line">  <span class="attr">secure</span>: <span class="literal">true</span>, <span class="comment">// 只能https进行设置和访问session，一般开发时设成false</span></span><br><span class="line">  <span class="attr">sameSite</span>: <span class="literal">null</span>, </span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 使用中间件</span></span><br><span class="line">app.use(session(CONFIG, app));</span><br><span class="line"></span><br><span class="line">app.use(<span class="function"><span class="params">ctx</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// ignore favicon</span></span><br><span class="line">  <span class="keyword">if</span> (ctx.path === <span class="string">&#x27;/favicon.ico&#x27;</span>) <span class="keyword">return</span>;</span><br><span class="line">    </span><br><span class="line">  <span class="comment">// 直接使用ctx.seesion进行访问</span></span><br><span class="line">  <span class="keyword">let</span> n = ctx.session.views || <span class="number">0</span>;</span><br><span class="line">  ctx.session.views = ++n;</span><br><span class="line">  ctx.body = n + <span class="string">&#x27; views&#x27;</span>;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>



<h3 id="数据库"><a href="#数据库" class="headerlink" title="数据库"></a>数据库</h3><p>这里以 <a target="_blank" rel="noopener" href="https://blog.csdn.net/bbsyi/article/details/86477959">mysql </a>为例，感觉这里跟上面的express使用mysql差不多，反正在npm里面都是同一个中间件</p>
<p>需要先安装mysql</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">npm install mysql --save</span><br></pre></td></tr></table></figure>

<p>然后创建一个配置文件，在配置文件里面存放数据相关的东西</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 设置配置文件</span></span><br><span class="line"><span class="keyword">const</span> config = &#123;</span><br><span class="line">    <span class="comment">// 启动端口</span></span><br><span class="line">    <span class="attr">port</span>: <span class="number">3000</span>,</span><br><span class="line">    <span class="comment">// 数据库配置</span></span><br><span class="line">    <span class="attr">database</span>: &#123;</span><br><span class="line">        <span class="attr">DATABASE</span>: <span class="string">&#x27;exapp&#x27;</span>,</span><br><span class="line">        <span class="attr">USERNAME</span>: <span class="string">&#x27;root&#x27;</span>,</span><br><span class="line">        <span class="attr">PASSWORD</span>: <span class="string">&#x27;123456&#x27;</span>,</span><br><span class="line">        <span class="attr">PORT</span>: <span class="string">&#x27;3306&#x27;</span>,</span><br><span class="line">        <span class="attr">HOST</span>: <span class="string">&#x27;localhost&#x27;</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = config</span><br></pre></td></tr></table></figure>

<p>然后在一个文件里面进行数据库连接</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> mysql = <span class="built_in">require</span>(<span class="string">&#x27;mysql&#x27;</span>)</span><br><span class="line"><span class="keyword">var</span> config = <span class="built_in">require</span>(<span class="string">&#x27;../uitl/sqlConfig&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> pool = mysql.createPool(&#123;</span><br><span class="line">    <span class="attr">host</span>: config.database.HOST,</span><br><span class="line">    <span class="attr">user</span>: config.database.USERNAME,</span><br><span class="line">    <span class="attr">password</span>: config.database.PASSWORD,</span><br><span class="line">    <span class="attr">database</span>: config.database.DATABASE,</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Mysql</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="title">constructor</span>(<span class="params"></span>)</span> &#123;&#125;</span><br><span class="line">    <span class="function"><span class="title">queryPost</span>(<span class="params">sqlStr, sqlArr = <span class="literal">null</span></span>)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">            <span class="comment">// 这里要注意一下，跟上面的express有些差别</span></span><br><span class="line">            pool.query(sqlStr, sqlArr, <span class="function"><span class="keyword">function</span> (<span class="params">error, results, fields</span>) </span>&#123;</span><br><span class="line">                <span class="keyword">if</span> (error) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> error</span><br><span class="line">                &#125;</span><br><span class="line">                resolve(results)</span><br><span class="line">            &#125;)</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="keyword">new</span> Mysql()</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>之后进行使用</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> Router = <span class="built_in">require</span>(<span class="string">&#x27;@koa/router&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> mysql = <span class="built_in">require</span>(<span class="string">&#x27;./connect&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> Router()</span><br><span class="line">router.get(<span class="string">&#x27;/sql&#x27;</span>, <span class="keyword">async</span> (ctx) =&gt; &#123;</span><br><span class="line">    <span class="keyword">let</span> sqlStr = <span class="string">&#x27;SELECT * from post where id= ?&#x27;</span></span><br><span class="line">    <span class="keyword">let</span> sqlArr = [<span class="number">2</span>]</span><br><span class="line">    <span class="keyword">let</span> data = <span class="keyword">await</span> mysql.queryPost(sqlStr, sqlArr)</span><br><span class="line">    ctx.body = &#123; <span class="attr">sql</span>: <span class="string">&#x27;sql of my&#x27;</span>, <span class="attr">data</span>: data &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

</div></article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="mailto:undefined">晟松</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://chengsong.info/2021/02/05/Nodejs-express-koa2初学习/">https://chengsong.info/2021/02/05/Nodejs-express-koa2初学习/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://chengsong.info">晟松</a>！</span></div></div><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/nodejs-express-koa2/">nodejs express koa2</a></div><nav id="pagination"><div class="prev-post pull-left"><a href="/2021/02/06/Nuxt%E8%BF%9B%E4%B8%80%E6%AD%A5%E5%AD%A6%E4%B9%A0/"><i class="fa fa-chevron-left">  </i><span>Nuxt进一步学习</span></a></div><div class="next-post pull-right"><a href="/2021/01/31/Nuxtjs%E5%88%9D%E5%AD%A6%E4%B9%A0/"><span>Nuxtjs初学习</span><i class="fa fa-chevron-right"></i></a></div></nav></div></div><footer class="footer-bg" style="background-image: url(/img/blog-bg.jpg)"><div class="layout" id="footer"><div class="copyright">&copy;2020 - 2023 By 晟松</div><div class="framework-info"><span>驱动 - </span><a target="_blank" rel="noopener" href="http://hexo.io"><span>Hexo</span></a><span class="footer-separator">|</span><span>主题 - </span><a target="_blank" rel="noopener" href="https://github.com/Molunerfinn/hexo-theme-melody"><span>Melody</span></a></div><div class="icp"><a target="_blank" rel="noopener" href="http://www.beian.miit.gov.cn/"><span>湘ICP备2020021729号</span></a></div><div class="busuanzi"><script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><span id="busuanzi_container_page_pv"><i class="fa fa-file"></i><span id="busuanzi_value_page_pv"></span><span></span></span></div></div></footer><i class="fa fa-arrow-up" id="go-up" aria-hidden="true"></i><script src="https://cdn.jsdelivr.net/npm/animejs@latest/anime.min.js"></script><script src="https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.js"></script><script src="https://cdn.jsdelivr.net/npm/velocity-animate@latest/velocity.min.js"></script><script src="https://cdn.jsdelivr.net/npm/velocity-ui-pack@latest/velocity.ui.min.js"></script><script src="/js/utils.js?version=1.9.0"></script><script src="/js/fancybox.js?version=1.9.0"></script><script src="/js/sidebar.js?version=1.9.0"></script><script src="/js/copy.js?version=1.9.0"></script><script src="/js/fireworks.js?version=1.9.0"></script><script src="/js/transition.js?version=1.9.0"></script><script src="/js/scroll.js?version=1.9.0"></script><script src="/js/head.js?version=1.9.0"></script><script src="/js/search/local-search.js"></script><script>if(/Android|webOS|iPhone|iPod|iPad|BlackBerry/i.test(navigator.userAgent)) {
  $('#nav').addClass('is-mobile')
  $('footer').addClass('is-mobile')
  $('#top-container').addClass('is-mobile')
}</script><div class="search-dialog" id="local-search"><div class="search-dialog__title" id="local-search-title">本地搜索</div><div id="local-input-panel"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章"></div></div></div><hr><div id="local-search-results"><div id="local-hits"></div><div id="local-stats"><div class="local-search-stats__hr" id="hr"><span>由</span> <a target="_blank" rel="noopener" href="https://github.com/wzpan/hexo-generator-search" style="color:#49B1F5;">hexo-generator-search</a>
 <span>提供支持</span></div></div></div><span class="search-close-button"><i class="fa fa-times"></i></span></div><div class="search-mask"></div></body></html>